(** Module Transform contains:
 *  - calls grammar transformations 
 *    ( converting EBNF to metarules
 *    , expanding metarules 
 *    , grammar simplification
 *    )
 *  - function, which transforms action code for inherited attributes support
 *
 *  Author: Ilia
 *)

open Misc
open Diagnostics

open TransformPredicates
open ExpandEBNF
open ExpandMeta
open SimplifyGrammar

(** for transform actions *)
open IL
open Production
open Generation
open Namer
open TransformAux

let createElkBind num = 
    Some (sprintf "%d" num ++ withElkPrefix ++ createSource)

let getActParams = function
    | PRef (_, p) -> p
    | _ -> None

let needInActions = function | (None, _, None) -> false | _ -> true

(** collect list of tuples: (<oldBind>, <newBind>, <params>) *)
let collectBindsAndParams item num =
    let newBind = createElkBind num in
    let params = getActParams item.rule in
    let tuple = ( item.binding, newBind, params ) in
    let item' = if item.binding <> None || params <> None
                then { item with binding = newBind }
                else item
    in ( tuple, item' )

let createFun = function
    | Some params -> sprintf "fun %s -> " (getText params)
    | None -> ""

let createLet (oldBind, newBind, params) =
    let oldBind' = 
      let bind = genOptText oldBind 
      in if bind = "" then "_" else bind
    in sprintf "let %s = %s %s in" oldBind' (genOptText newBind) 
                                   (genOptText params)

let createActionText l params =
    let funAct = createFun params in
    let actBody = String.concat "\n" (List.map createLet l)
    in sprintf "%s%s" funAct actBody

let createOptAction text = 
    if text <> "" then Some ( text ++ createSource )
    else None

let createAction actionPrefix actText =
    sprintf "%s (%s)" actionPrefix actText ++ createOptAction

let transformAction action params = 
    let actText = genOptText action 
    in function
    | [] -> createAction (createFun params) actText
    | l  -> createAction (createActionText l params) actText

let rec transformRuleAction args = function
    | PAlt (l, r) -> 
      let l' = transformRuleAction args l
      and r' = transformRuleAction args r
      in PAlt(l', r')
    | PSeq (seq, a) ->  
      let rec processSeq num = function
        | [] -> ([], [])
        | h::t -> 
          let (tuple, h') = collectBindsAndParams h num in
          let (l, elems) = processSeq (succ num) t in
          let l' = if needInActions tuple then tuple::l else l 
          in (l', h'::elems)
      in let (tuples, seq') = processSeq 0 seq in 
      let a' = transformAction a args tuples 
      in PSeq (seq', a')
    | other -> other

let rec transformActions = function
    | []    -> []
    | h::t  -> let args' = createOpt h.Rule.args in
      let h' = { h with Rule.body = transformRuleAction args' h.Rule.body }
      in h'::(transformActions t)

(** main function 
 *     transform predicates (created new rule with special parameters 
       and action code for each predicate)
 *  -> expand metarules (this need do first for expanding metarules 
 *     or their arguments inside EBNF constructions) ->
 *  -> convert EBNF constructions to metarules ->
 *  -> expand metarules (obtained after converting EBNF constructions) ->
 *  -> simplify (expand alternatives) ->
 *  -> transform action code (for support of inherited attributes)
 *  -> reverse list of rules 
 *     (Elkhound requires that start rule is first rule in grammar file)
 *)
let transform = function 
    | [] -> []
    | l  -> transformPredicates l (*dumpIL "transformed.dump" l;*)
            ++ expandMetaRules 
            ++ convertEBNFtoMeta 
            ++ expandMetaRules 
            ++ simplifyRules 
            ++ transformActions 
            ++ List.rev
